package com.bong.panpan.bongcharttools.adapter;

import android.content.Context;
import android.util.ArrayMap;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import com.bong.panpan.bongcharttools.R;
import com.bong.panpan.bongcharttools.ble.BleInfo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

/**
 * Created by panpan on 2017/9/25.
 */

public class DeviceListAdapter extends BaseAdapter {
    private Context context;
    private View.OnClickListener listener;
    private final ArrayList<BleInfo> listDataTemp = new ArrayList<>();
    private final ArrayMap<String, Integer> listDataIndexTemp = new ArrayMap<>();
    private ArrayList<BleInfo> listData = listDataTemp;
    private ArrayMap<String, Integer> listDataIndex = listDataIndexTemp;
    private final ArrayList<BleInfo> listDataFilter = new ArrayList<>();
    private final ArrayMap<String, Integer> listDataIndexFilter = new ArrayMap<>();
    private MyComparator comparator;
    private String filter = "";
    private boolean isFilter = false;

    public DeviceListAdapter(Context context) {
        this.context = context;
        comparator = new MyComparator();
    }

    @Override
    public int getCount() {
        return listData.size();
    }

    @Override
    public BleInfo getItem(int position) {
        return listData.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            holder = new ViewHolder();
            convertView = LayoutInflater.from(context).inflate(R.layout.list_item_device, parent, false);
            holder.image = (ImageView) convertView.findViewById(R.id.image);
            holder.name = (TextView) convertView.findViewById(R.id.name);
            holder.addr = (TextView) convertView.findViewById(R.id.mac);
            holder.rssi = (TextView) convertView.findViewById(R.id.rssi);
            holder.linkBtn = (Button) convertView.findViewById(R.id.link_btn);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        BleInfo info = listData.get(position);
        holder.linkBtn.setId(position);
        switch (info.state) {
            case DEVICE_STATE_LINKING:
                holder.linkBtn.setText("正在连接");
                break;
            case DEVICE_STATE_LINKED:
                holder.linkBtn.setText("断开连接");
                break;
            case DEVICE_STATE_DISLINK:
                holder.linkBtn.setText("连接");
                break;
            default:
                holder.linkBtn.setText("请稍后");
                break;
        }
        if (listener != null)
            holder.linkBtn.setOnClickListener(listener);
        if (info.name == null)
            holder.name.setText("N/A");
        else
            holder.name.setText(info.name);
        if (info.type == 0)
            holder.image.setImageResource(R.mipmap.bluetooth);
        else
            holder.image.setImageResource(R.mipmap.heart_icon);
        holder.addr.setText(info.mac);
        holder.rssi.setText(info.rssi + "dBm");
        return convertView;
    }

    public void setClickListener(View.OnClickListener listener) {
        this.listener = listener;
    }

    public class ViewHolder {
        ImageView image;
        TextView name, addr, rssi;
        Button linkBtn;
    }

    public synchronized void addData(BleInfo info) {
        listDataTemp.add(info);
        listDataIndexTemp.put(info.mac, listDataTemp.size() - 1);
        if (isFilter) {
            if (isFilterOk(info)) {
                listDataFilter.add(info);
                listDataIndexFilter.put(info.mac, listDataTemp.size() - 1);
            }
            //refreshFilterData();
        }
    }

    private synchronized void remove(int position) {
        if (position < 0 || position > listDataTemp.size() - 1)
            return;
        listDataTemp.remove(position);
        listDataIndexTemp.removeAt(position);
        for (int i = position; i < listDataTemp.size(); i++) {
            listDataIndexTemp.setValueAt(i, listDataIndexTemp.valueAt(i) - 1);
        }
    }

    public synchronized void removeData(int... positions) {
        for (int i = 0; i < positions.length; i++) {
            remove(positions[i]);
        }
    }

    public synchronized void removeData(String addr) {
        int index = listDataIndexTemp.get(addr);
        remove(index);
        if (isFilter) {
            if (!listDataIndexFilter.containsKey(addr))
                return;
            index = listDataIndexFilter.get(addr);
            if (index > -1) {
                listDataFilter.remove(index);
                listDataIndexFilter.removeAt(index);
                for (int i = index; i < listDataFilter.size(); i++) {
                    listDataIndexFilter.setValueAt(i, listDataIndexFilter.valueAt(i) - 1);
                }
            }
            //refreshFilterData();
        }
    }

    public synchronized void clear() {
        listDataTemp.clear();
        listDataIndexTemp.clear();
        listDataFilter.clear();
        listDataIndexFilter.clear();
    }

    public synchronized void modifyData(String addr, int rssi) {
        int index = listDataIndexTemp.get(addr);
        if (index > -1)
            listDataTemp.get(index).rssi = rssi;
        if (isFilter) {
            if (!listDataIndexFilter.containsKey(addr))
                return;
            index = listDataIndexFilter.get(addr);
            Log.i("fuck", "index=" + index);
            if (index > -1)
                listDataFilter.get(index).rssi = rssi;
            //refreshFilterData();
        }
    }

    public boolean isDataExits(String addr) {
        return listDataIndexTemp.containsKey(addr);
    }

    public synchronized void sortData(int model) {
        if (model > 1)
            return;
        comparator.model = model;
        Collections.sort(listDataTemp, comparator);
        for (int i = 0; i < listDataTemp.size(); i++) {
            listDataIndexTemp.put(listDataTemp.get(i).mac, i);
        }
        if (isFilter) {
            Collections.sort(listDataFilter, comparator);
            for (int i = 0; i < listDataFilter.size(); i++) {
                listDataIndexFilter.put(listDataFilter.get(i).mac, i);
            }
            //refreshFilterData();
        }
    }

    private boolean isFilterOk(BleInfo info) {
        return (info.name != null && info.name.toLowerCase().contains(filter)) || info.mac.toLowerCase().contains(filter);
    }

    private synchronized void refreshFilterData() {
        listDataFilter.clear();
        listDataIndexFilter.clear();
        BleInfo info;
        for (int i = 0; i < listDataTemp.size(); i++) {
            info = listDataTemp.get(i);
            if (isFilterOk(info)) {
                listDataFilter.add(info);
                listDataIndexFilter.put(info.mac, listDataFilter.size() - 1);
            }
        }
    }

    public synchronized void setFilter(String filter) {
        filter = filter.toLowerCase();
        if (filter == null || "".equals(filter)) {
            listData = listDataTemp;
            listDataIndex = listDataIndexTemp;
            isFilter = false;
            return;
        }
        if (!this.filter.equals(filter)) {
            this.filter = filter;
            refreshFilterData();
        }
        listData = listDataFilter;
        listDataIndex = listDataIndexFilter;
        isFilter = true;
    }

    class MyComparator implements Comparator {

        /**
         * Compares its two arguments for order.  Returns a negative integer,
         * zero, or a positive integer as the first argument is less than, equal
         * to, or greater than the second.<p>
         * <p>
         * In the foregoing description, the notation
         * <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
         * <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
         * <tt>0</tt>, or <tt>1</tt> according to whether the value of
         * <i>expression</i> is negative, zero or positive.<p>
         * <p>
         * The implementor must ensure that <tt>sgn(compare(x, y)) ==
         * -sgn(compare(y, x))</tt> for all <tt>x</tt> and <tt>y</tt>.  (This
         * implies that <tt>compare(x, y)</tt> must throw an exception if and only
         * if <tt>compare(y, x)</tt> throws an exception.)<p>
         * <p>
         * The implementor must also ensure that the relation is transitive:
         * <tt>((compare(x, y)&gt;0) &amp;&amp; (compare(y, z)&gt;0))</tt> implies
         * <tt>compare(x, z)&gt;0</tt>.<p>
         * <p>
         * Finally, the implementor must ensure that <tt>compare(x, y)==0</tt>
         * implies that <tt>sgn(compare(x, z))==sgn(compare(y, z))</tt> for all
         * <tt>z</tt>.<p>
         * <p>
         * It is generally the case, but <i>not</i> strictly required that
         * <tt>(compare(x, y)==0) == (x.equals(y))</tt>.  Generally speaking,
         * any comparator that violates this condition should clearly indicate
         * this fact.  The recommended language is "Note: this comparator
         * imposes orderings that are inconsistent with equals."
         *
         * @param o1 the first object to be compared.
         * @param o2 the second object to be compared.
         * @return a negative integer, zero, or a positive integer as the
         * first argument is less than, equal to, or greater than the
         * second.
         * @throws NullPointerException if an argument is null and this
         * comparator does not permit null arguments
         * @throws ClassCastException   if the arguments' types prevent them from
         * being compared by this comparator.
         */
        int model = 0;

        @Override
        public int compare(Object o1, Object o2) {
            if (model == 0)
                return ((BleInfo) o2).rssi - ((BleInfo) o1).rssi;
            return ((BleInfo) o1).rssi - ((BleInfo) o2).rssi;
        }
    }
}
